package cn.runagain.demo.xianliu;

import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;

/**
 * 漏桶思想，有一个漏水桶，出水率是恒定的，进水的速率是不确定的，但是当进水超过出水率的时候，水直接溢出。
 * <p>
 * 令牌桶限流，根据漏桶的算法来生成令牌，请求需要先获取令牌，
 * 这样做的目的是，如果直接采取漏桶算法，那么，溢出的请求直接被丢弃了，这显然是不合理的。
 * 所以将获取令牌的过程采用漏桶算法来管理，请求处理的先获取令牌。
 * <p>
 * Guava RateLimiter基于令牌桶算法，只需要告诉RateLimiter系统限制的QPS是多少，
 * 那么RateLimiter将以这个速度往桶里面放入令牌，然后请求的时候，
 * 通过tryAcquire()方法向RateLimiter获取许可（令牌).
 *
 * @author tuxianchao
 * @date 2018/6/17 上午12:08
 */
public class RateLimiter {
    /**
     * 资源限制器集合
     */
    private static ConcurrentHashMap<String, com.google.common.util.concurrent.RateLimiter> resourceLimiter =
            new ConcurrentHashMap<>();

    static {
        createResourcesLimiter("orderCenter", 300);
    }

    /**
     * 初始化rateLimiter
     *
     * @param resource 资源名
     * @param qps      qps
     */
    public static void createResourcesLimiter(String resource, double qps) {

        if (resourceLimiter.contains(resource)) {
            resourceLimiter.get(resource).setRate(qps);
        } else {
            com.google.common.util.concurrent.RateLimiter rateLimiter = com.google.common.util.concurrent.RateLimiter.create(qps);
            resourceLimiter.putIfAbsent(resource, rateLimiter);
        }
    }

    public static void main(String[] args) {
//       测试
        for (int i = 0; i < 1000; i++) {
            new Thread(() -> {
                //获取令牌
                if (resourceLimiter.get("orderCenter").tryAcquire(10, TimeUnit.MILLISECONDS)) {
                    System.out.println("执行订单中心业务逻辑...");
                } else {
                    System.out.println("订单中心业务限流...");
                }
            }).start();
        }

        new Thread(() -> {
            //TODO do something
        }).start();
    }
}
